package com.mpsc.flowable.modeler.controller;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mpsc.flowable.modeler.domain.ProcNodeInfo;
import com.mpsc.flowable.modeler.service.DeployService;
import com.mpsc.flowable.modeler.util.DateUtil;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.*;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.editor.language.json.converter.BpmnJsonConverter;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.ui.modeler.domain.Model;
import org.flowable.ui.modeler.serviceapi.ModelService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.*;

@RestController
@RequestMapping("/deploy")
public class DeployController {
    private static final Logger LOGGER = LoggerFactory.getLogger(DeployController.class);
    @Autowired
    private DeployService deployService;
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private ModelService modelService;


    @GetMapping("/process/model/{modelId}")
    public void deployProcessModelByModelId(@PathVariable String modelId) throws IOException {
        Model modelData = modelService.getModel(modelId);
        JsonNode modelNode = new ObjectMapper().readTree(modelData.getModelEditorJson());
        BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
        BpmnModel model = jsonConverter.convertToBpmnModel(modelNode);
        byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);
        if (model.getProcesses().size() == 0) {
            throw new FlowableException("流程模型不符要求，请至少设计一条主线流程!");
        }
        String processName = modelData.getName() + ".bpmn20.xml";
        Deployment deployment = repositoryService.createDeployment()
                .name(modelData.getName())
                .addString(processName, new String(bpmnBytes, "UTF-8"))
                .deploy();
        parseBpmModel(deployment.getId(),model,modelData,modelNode);
    }

    /**
     * 流程图解析
     *
     * @param deployId
     * @param bpmnModel
     */
    public void parseBpmModel(String deployId, BpmnModel bpmnModel,Model modelData,JsonNode modelNode) {
        LOGGER.info("流程部署进入流程图解析方法parseBpmModel，deployId：{}",deployId);
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deployId)
                .singleResult();
        if (processDefinition != null) {
            LOGGER.info("流程部署进入流程图解析方法parseBpmModel，processDefinition：{}",processDefinition);
            //获取流程定义Id
            String procDefId = processDefinition.getId();
            String procDefKey = processDefinition.getKey();
            //解析
            org.flowable.bpmn.model.Process process = bpmnModel.getMainProcess();
            Collection<FlowElement> flowElements = process.getFlowElements();
            //流程的第一个节点的Id(非开始节点)
            String firstNodeId = "";
            for (FlowElement element : flowElements) {
                //流程图节点属性
                ProcNodeInfo entity = new ProcNodeInfo();
                entity.setId(UUID.randomUUID().toString());
                //TODO 暂时过滤掉线类型，后期做流程线扩展
                if (element instanceof SequenceFlow) {
                    SequenceFlow sequenceFlow = (SequenceFlow) element;
                    saveSequenceFlowVariables(procDefId,element,modelNode);
                    //保存线上配置的业务规则
                    //saveSequenceFlowBizRules(procDefId,element,modelNode);
                    if(StringUtils.isNotBlank(sequenceFlow.getConditionExpression())) {
                        entity.setNodeType("sequenceFlow");
                        entity.setNodeName(element.getName());
                        entity.setPhysicsParentId(((SequenceFlow) element).getSourceRef());
                        entity.setPhysicsChildId(((SequenceFlow) element).getTargetRef());
                        String conditionExpression = sequenceFlow.getConditionExpression().replace("${","");
                        String newconditionExpression = conditionExpression.replace("}","");
                        entity.setConditionExpress(newconditionExpression);
                    }else {
                        continue;
                    }
                }
                //流程定义Id
                entity.setProcDefId(procDefId);
                entity.setNodeId(element.getId());
                entity.setCreateTime(DateUtil.getCurrentTime());
                //用户任务节点类型
                if (element instanceof UserTask) {
                    UserTask userTask = (UserTask) element;
                    userTask.getIncomingFlows().get(0);
                    entity.setNodeName(element.getName());
                    entity.setNodeType("userTask");
                    List<ExtensionAttribute> userList = userTask.getAttributes().get("flowable-ext-user");
                   // saveProcIdentity(userList,element,procDefId, RestIdentity.IDENTITY_USER,"人员");
                    List<ExtensionAttribute> roleList = userTask.getAttributes().get("flowable-ext-role");
                    //saveProcIdentity(roleList,element,procDefId, RestIdentity.IDENTITY_ROLE,"角色");
                    List<ExtensionAttribute> unitList = userTask.getAttributes().get("flowable-ext-unit");
                    //saveProcIdentity(unitList,element,procDefId,RestIdentity.IDENTITY_UNIT,"单位");
                    List<ExtensionAttribute> deptList = userTask.getAttributes().get("flowable-ext-dept");
                    //saveProcIdentity(deptList,element,procDefId,RestIdentity.IDENTITY_DEPT,"部门");
                    List<ExtensionAttribute> deptLeaderList = userTask.getAttributes().get("flowable-ext-dept-leader");
                    //saveProcIdentity(deptLeaderList,element,procDefId,RestIdentity.IDENTITY_DEPT_LEADER,"部门负责人");
                    List<ExtensionAttribute> menuList = userTask.getAttributes().get("flowable-ext-menu");
                    //saveMenu(menuList,element,procDefId,procDefKey);
                    List<ExtensionAttribute> taskEventList = userTask.getAttributes().get("flowable-ext-task-event");
                    //saveEvent(taskEventList,"002",element,procDefId,procDefKey);
                    List<ExtensionAttribute> executionEventList = userTask.getAttributes().get("flowable-ext-execution-event");
                    //saveEvent(executionEventList,"001",element,procDefId,procDefKey);
                    List<ExtensionAttribute> ruleList = userTask.getAttributes().get("flowable-ext-execution-rule");
                    //saveRules(ruleList,element,procDefId,procDefKey);
                    //保存配置的驳回规则
                    List<ExtensionAttribute> backRuleList = userTask.getAttributes().get("flowable-ext-execution-back-rule");
                    //saveBackRules(backRuleList,element,procDefId,procDefKey);
                    List<ExtensionAttribute> customVariables = userTask.getAttributes().get("flowable-ext-custom_variables");
                    //saveCustomVariables(customVariables,procDefId,element);

                    //判断是否多实例节点,多实例节点标记为1
                    if (userTask.getLoopCharacteristics() != null) {
                        entity.setIsMultiinstance("1");
                    } else {
                        entity.setIsMultiinstance("0");
                    }
                    List<ExtensionAttribute> preemptmodeList = userTask.getAttributes().get("flowable-ext-preemptmode");
                    for(ExtensionAttribute attribute : preemptmodeList){
                        if(attribute.getName().equals("preemptmode")){
                            String preemptmode = attribute.getValue();
                            if(preemptmode.equalsIgnoreCase("true")){
                                entity.setIsPreemptMode("1");
                            }else{
                                entity.setIsPreemptMode("0");
                            }
                        }
                    }
                    List<ExtensionAttribute> checkusersList = userTask.getAttributes().get("flowable-ext-checkusers");
                    for(ExtensionAttribute attribute: checkusersList){
                        if(attribute.getName().equals("checkusers")){
                            String checkusers = attribute.getValue();
                            if(checkusers.equalsIgnoreCase("true")){
                                entity.setIsCheckUsers("1");
                            }else{
                                entity.setIsCheckUsers("0");
                            }
                        }
                    }
                    //记账标识
                    List<ExtensionAttribute> accountFlagList = userTask.getAttributes().get("flowable-ext-accountflag");
                    if(accountFlagList != null){
                        for(ExtensionAttribute attribute: accountFlagList){
                            if(attribute.getName().equals("accountflag")){
                                String accountflag = attribute.getValue();
                                if(accountflag.equalsIgnoreCase("1")){
                                    entity.setAccountFlag("1");
                                }else if(accountflag.equalsIgnoreCase("0")){
                                    entity.setAccountFlag("0");
                                }
                            }
                        }
                    }
                    // 审核类型
                    List<ExtensionAttribute> approveTypeList = userTask.getAttributes().get("flowable-ext-approvetype");
                    if(approveTypeList != null){
                        for(ExtensionAttribute attribute: approveTypeList){
                            if(attribute.getName().equals("approvetype")){
                                String approveType = attribute.getValue();
                                entity.setApproveType(approveType);
                            }
                        }
                    }
                    //获取物理、逻辑父节点
                    List<String> physicsParentIds = findPhysicsParentIds(element, flowElements);
                    String physicsParentId = convertList2Str(physicsParentIds);
                    String logicParentId = convertList2Str(findLogicParentIds(physicsParentIds, element, flowElements));
                    entity.setLogicParentId(logicParentId);
                    entity.setPhysicsParentId(physicsParentId);
                    //是否设置驳回
                    List<ExtensionAttribute> reject = userTask.getAttributes().get("flowable-ext-isReject");
                    if(reject !=null && reject.size()>0){
                        for(ExtensionAttribute attribute: reject){
                            if(attribute.getName().equals("isReject")){
                                String isReject = attribute.getValue();
                                if(isReject.equalsIgnoreCase("true")){
                                    entity.setIsReject("1");
                                }else{
                                    entity.setIsReject("0");
                                }
                            }
                        }
                    }
                    //驳回设置
                    List<ExtensionAttribute> rejectNodeSetting = userTask.getAttributes().get("flowable-ext-rejectNodeSetting");
                    //保存驳回设置
                    //saveRejectSetting(rejectNodeSetting,element,procDefId,logicParentId,firstNodeId);
                    //物理子节点
                    List<String> phsicsChildIds = getPhsicsChildIds(element);
                    entity.setPhysicsChildId(convertList2Str(phsicsChildIds));
                } else if (element instanceof StartEvent) {
                    if (StringUtils.isBlank(element.getName())) {
                        entity.setNodeName("开始");
                    } else {
                        entity.setNodeName(element.getName());
                    }
                    entity.setIsMultiinstance("0");
                    entity.setNodeType("startEvent");
                    firstNodeId = ((StartEvent) element).getOutgoingFlows().get(0).getTargetRef();
                    //物理子节点
                    List<String> phsicsChildIds = getPhsicsChildIds(element);
                    entity.setPhysicsChildId(convertList2Str(phsicsChildIds));
                } else if (element instanceof EndEvent) {
                    if (StringUtils.isBlank(element.getName())) {
                        entity.setNodeName("结束");
                    } else {
                        entity.setNodeName(element.getName());
                    }
                    entity.setIsMultiinstance("0");
                    entity.setNodeType("endEvent");
                    //获取物理、逻辑父节点
                    List<String> physicsParentIds = findPhysicsParentIds(element, flowElements);
                    String physicsParentId = convertList2Str(physicsParentIds);
                    String logicParentId = convertList2Str(findLogicParentIds(physicsParentIds, element, flowElements));
                    entity.setLogicParentId(logicParentId);
                    entity.setPhysicsParentId(physicsParentId);
                    // 保存执行监听器事件
                    List<ExtensionAttribute> executionEventList = element.getAttributes().get("flowable-ext-execution-event");
                    //saveEvent(executionEventList,"001",element,procDefId,procDefKey);
                } else if (element instanceof Gateway) {
                    if (StringUtils.isBlank(element.getName())) {
                        entity.setNodeName("网关");
                    } else {
                        entity.setNodeName(element.getName());
                    }
                    List<String> physicsParentIds = findPhysicsParentIds(element, flowElements);
                    String physicsParentId = convertList2Str(physicsParentIds);
                    entity.setPhysicsParentId(physicsParentId);
                    entity.setIsMultiinstance("0");
                    entity.setNodeType(element.getClass().getSimpleName());
                    //物理子节点
                    List<String> phsicsChildIds = getPhsicsChildIds(element);
                    entity.setPhysicsChildId(convertList2Str(phsicsChildIds));
                }
                //procNodeService.save(entity);
            }
           // saveProcessVariables(modelNode,procDefId);
            //刷新缓存数据
            // 只取非共享的rgCode，共享的rgCode可能很多，就不存redis了
//            if(!modelData.getIsShare()) {
//                String rgCode=modelData.getRegion();
//                String unitCode=modelData.getAgency();
//                String bizTypeCode = modelData.getBizTypeCode();
//                refreshProcDefIdOfRedis(rgCode,unitCode,bizTypeCode,procDefId);
//            }
        }else {
            LOGGER.info("流程部署进入流程图解析方法parseBpmModel，processDefinition为null");

        }
    }

    /**
     * 查询物理子节点Id
     * @param element
     * @return
     */
    private List<String> getPhsicsChildIds(FlowElement element){
        List<String> physicsChildIds = new ArrayList<String>();
        if (element instanceof UserTask) {
            UserTask userTask = (UserTask) element;
            List<SequenceFlow> outGoingFlows = userTask.getOutgoingFlows();
            if(outGoingFlows !=null && !outGoingFlows.isEmpty()){
                for(SequenceFlow flow : outGoingFlows){
                    physicsChildIds.add(flow.getTargetRef());
                }
            }
        }
        if (element instanceof Gateway) {
            Gateway gateWay = (Gateway) element;
            List<SequenceFlow> outGoingFlows = gateWay.getOutgoingFlows();
            if(outGoingFlows !=null && !outGoingFlows.isEmpty()){
                for(SequenceFlow flow : outGoingFlows){
                    physicsChildIds.add(flow.getTargetRef());
                }
            }
        }
        if (element instanceof StartEvent) {
            StartEvent startEvent = (StartEvent)element;
            List<SequenceFlow> outGoingFlows = startEvent.getOutgoingFlows();
            if(outGoingFlows !=null && !outGoingFlows.isEmpty()){
                for(SequenceFlow flow : outGoingFlows){
                    physicsChildIds.add(flow.getTargetRef());
                }
            }
        }
        return physicsChildIds;
    }

    /**
     * 返回节点的物理父节点id
     *
     * @param element
     * @param flowElements
     * @return
     */
    private List<String> findPhysicsParentIds(FlowElement element, Collection<FlowElement> flowElements) {
        List<String> physicsParentIds = new ArrayList<String>();
        if (element instanceof UserTask) {
            UserTask userTask = (UserTask) element;
            List<SequenceFlow> inComigFlows = userTask.getIncomingFlows();
            for (SequenceFlow flow : inComigFlows) {
                String sourceRef = flow.getSourceRef();
                physicsParentIds.add(sourceRef);
            }
        }
        if (element instanceof Gateway) {
            Gateway gateWay = (Gateway) element;
            List<SequenceFlow> inComigFlows = gateWay.getIncomingFlows();
            for (SequenceFlow flow : inComigFlows) {
                String sourceRef = flow.getSourceRef();
                physicsParentIds.add(sourceRef);
            }
        }
        if (element instanceof EndEvent) {
            EndEvent endEvent = (EndEvent) element;
            List<SequenceFlow> inComigFlows = endEvent.getIncomingFlows();
            for (SequenceFlow flow : inComigFlows) {
                String sourceRef = flow.getSourceRef();
                physicsParentIds.add(sourceRef);
            }
        }
        return physicsParentIds;
    }

    /**
     * list转换成str 元素之间用,分割
     *
     * @param physicsParentIds
     * @return
     */
    private String convertList2Str(List<String> physicsParentIds) {
        StringBuffer pysicsParentIds = new StringBuffer();
        Iterator<String> it = physicsParentIds.iterator();
        while (it.hasNext()) {
            pysicsParentIds.append(it.next());
            pysicsParentIds.append(",");
        }
        if (StringUtils.isNotEmpty(pysicsParentIds)) {
            return pysicsParentIds.subSequence(0, pysicsParentIds.length() - 1).toString();
        }
        return "";
    }

    /**
     * 返回节点的逻辑父节点id
     *
     * @param physicsParentIds
     * @param flowElements
     * @return
     */
    public List<String> findLogicParentIds(List<String> physicsParentIds, FlowElement element, Collection<FlowElement> flowElements) {
        List<String> logicParentIds = new ArrayList<String>();
        Iterator<String> it = physicsParentIds.iterator();
        while (it.hasNext()) {
            String physicsParentId = it.next();
            //判断该parentId对应的节点是否是网关，如果不是网关,它就是逻辑父id;如果是网关，需要再往上找，直到找到非网关的节点
            if (!isGateWay(physicsParentId, flowElements)) {
                logicParentIds.add(physicsParentId);
            } else {
                findLogicParentIdForGateWay(physicsParentId, flowElements, logicParentIds);
            }
        }
        return logicParentIds;
    }
    /**
     * 找到网关的逻辑父节点
     *
     * @param id           网关id
     * @param flowElements
     * @return 该网关的逻辑父节点
     */
    private List<String> findLogicParentIdForGateWay(String id, Collection<FlowElement> flowElements, List<String> logicParentIds) {
        for (FlowElement element : flowElements) {
            if (id.equals(element.getId()) && element instanceof Gateway) {
                Gateway gateWay = (Gateway) element;
                List<SequenceFlow> flows = gateWay.getIncomingFlows();
                for (SequenceFlow flow : flows) {
                    String sourceRef = flow.getSourceRef();
                    if (!isGateWay(sourceRef, flowElements)) {
                        if(!logicParentIds.contains(sourceRef)){
                            logicParentIds.add(sourceRef);
                        }
                    } else {
                        //递归查找，直到找到非网关节点(最坏的情况是一直到开始节点)
                        findLogicParentIdForGateWay(sourceRef, flowElements, logicParentIds);
                    }
                }
            }
        }
        return logicParentIds;
    }

    /**
     * 判断对应的节点是否是网关类型的
     *
     * @param id           节点id
     * @param flowElements
     * @return 如果是网关类型, 则返回true, 否则返回false
     */
    private boolean isGateWay(String id, Collection<FlowElement> flowElements) {
        for (FlowElement element : flowElements) {
            if (id.equals(element.getId())) {
                if (element instanceof Gateway) {
                    return true;
                } else {
                    return false;
                }
            }
        }
        return false;
    }
    /**
     * 保存线上的扩展属性集集合
     *
     * @param element
     * @param procDefId
     */
    private void saveSequenceFlowVariables(String procDefId, FlowElement element,JsonNode modelNode) {
        try {
            JsonNode modelJSONNode = modelNode.get("childShapes");
           // CustomVariable customVariable;
            if (modelJSONNode != null && modelJSONNode.size() != 0) {
                for (int i = 0; i < modelJSONNode.size(); i++) {
                    JsonNode childNode = modelJSONNode.get(i);
                    String resourceId = childNode.get("resourceId").asText();
                    // 找到对应的线了
                    if (element.getId().equals(resourceId)) {
                        JsonNode propertiesNode = childNode.get("properties");
                        if (propertiesNode != null) {
                            JsonNode customVariables = propertiesNode.get("custom_variables");
                            if(customVariables != null){
                                if (customVariables.get("variables") != null) {
                                    JsonNode variables = customVariables.get("variables");
                                    if (variables != null && variables.size() != 0) {
                                        for (int j = 0; j < variables.size(); j++) {
//                                            customVariable = new CustomVariable();
//                                            JsonNode variable = variables.get(j);
//                                            customVariable.setId(UUID.randomUUID().toString());
//                                            customVariable.setProcDefId(procDefId);
//                                            customVariable.setNodeId(element.getId());
//                                            customVariable.setVariableName(variable.get("key").asText());
//                                            customVariable.setVariableValue(variable.get("value").asText());
//                                            procCustomVariableService.save(customVariable);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
